/* jshint esversion: 6 */
console.log('%c MMD %c 网页MMD解析框架\n %c作者: 小瑞猫',
    'font-family: "Helvetica Neue",Helvetica, Arial, sans-serif;font-size:64px;color:#FFB6C1;-webkit-text-fill-color:#FFB6C1;-webkit-text-stroke: 1px #FFB6C1;',
    'font-size:25px;color:#FFB6C1;', 'font-size:15px;color:#FFB6C1;');
// MMD 加载库
var MMD = function (scene) {
    var $ = this;
    $.scene = $.scene || scene;// 获得场景
    // Pmd 模型加载
    this.LoaderPmd = function (url, onLoad) {
        $.Load(
            url,
            'arraybuffer',
            undefined,
            function (buffer) {
                var pmd = $.Parser.parsePmd(buffer);
                pmd.url = url;
                onLoad($.ModelParsed(pmd, $.scene));
            }
        );
    };
    // Pmx 模型加载
    this.LoaderPmx = function (url, onLoad) {
        $.Load(
            url,
            'arraybuffer',
            undefined,
            function (buffer) {
                var pmx = $.Parser.parsePmx(buffer);
                pmx.url = url;
                onLoad($.ModelParsed(pmx, $.scene));
            }
        );
    };
};
// MMD 模型解析库
MMD.prototype.Parser = new MMDParser.Parser();
// MMD 默认贴图
MMD.prototype.Maps = [
    '',
    '',
    '',
    '',
    '',
    '',
    '',
    '',
    '',
    '',
    ''
];
//  文件加载器
MMD.prototype.Load = function (url, responseType, mimeType, onLoad, onProgress, onError) {
    var request = new XMLHttpRequest();
    request.open('GET', url, true);
    request.addEventListener('load', function (event) {
        var response = event.target.response;
        if (this.status === 200) {
            onLoad(response);
        } else if (this.status === 0) {
            console.warn('HTTP Status 0 received.');
            onLoad(response);
        } else {
            console.warn('HTTP Status ' + this.status + ' received.');
            onError(event);
        }
    }, false);
    if (onProgress !== undefined) request.addEventListener('progress', onProgress, false);
    if (onError !== undefined) request.addEventListener('error', onError, false);
    request.responseType = responseType;
    if (mimeType !== undefined) request.overrideMimeType(mimeType);
    request.send(null);
    console.log('加载文件: ' + url);
};
//  PMD/PMX 数据解析
MMD.prototype.ModelParsed = function (data, scene) {
    var $ = this;
    console.log(data);
    ////////-------- 材质解析 --------//////// 
    var textures = [];
    (() => {
        // 获得材质文件
        var url = data.url;
        url = url.slice(0, url.lastIndexOf("\\") == -1 ? url.lastIndexOf("/") : url.lastIndexOf("\\"));
        if (data.metadata.format === 'pmx') {
            for (let i = 0; i < data.textures.length; i++) {
                var textmat = new BABYLON.Texture(url + "/" + data.textures[i]);
                textmat.hasAlpha = true;
                textures.push(textmat);
            }
        } else {
            for (let i = 0; i < data.materials.length; i++) {
                var fileName = data.materials[i].fileName;
                var fileNames = fileName.split('*');
                var textmat = new BABYLON.Texture(url + "/" + fileNames[0]);
                textmat.hasAlpha = true;
                textures[fileNames[0]] = textmat;
                if (fileNames.length > 1) {
                    var textmat = new BABYLON.Texture(url + "/" + fileNames[1]);
                    textmat.hasAlpha = true;
                    textures[fileNames[1]] = textmat;
                }
            }
        }
    })();
    ////////-------- 参数解析 --------//////// 
    // 顶点缓存
    var uvs = [],
        indices = [],
        normals = [],
        positions = [],
        matricesIndices = [],
        matricesWeights = [];
    // 解析顶点
    for (let i = 0, k = data.vertices.length; i < k; i++) {
        let v = data.vertices[i];
        uvs.push(v.uv[0], 1 - v.uv[1]);
        normals.push(...v.normal);
        positions.push(...v.position);
        matricesIndices.push(...([...v.skinIndices, 0.0, 0.0, 0.0, 0.0].slice(0, 4)));
        matricesWeights.push(...([...v.skinWeights, 0.0, 0.0, 0.0, 0.0].slice(0, 4)));
    }
    // 解析面
    for (let i = 0, k = data.faces.length; i < k; i++) {
        indices.push(...data.faces[i].indices);
    }
    // iks
    var iks = [];
    if (data.metadata.format === 'pmd') {
        for (var i = 0; i < data.metadata.ikCount; i++) {
            var ik = data.iks[i];
            var param = {
                target: ik.target,
                effector: ik.effector,
                iteration: ik.iteration,
                maxAngle: ik.maxAngle * 4,
                links: []
            };
            for (var j = 0, jl = ik.links.length; j < jl; j++) {
                var link = {};
                link.index = ik.links[j].index;
                link.enabled = true;
                if (data.bones[link.index].name.indexOf('ひざ') >= 0) {
                    link.limitation = new BABYLON.Vector3(1.0, 0.0, 0.0);
                }
                param.links.push(link);
            }
            iks.push(param);
        }
    } else {
        for (let i = 0; i < data.metadata.boneCount; i++) {
            let ik = data.bones[i].ik;
            if (ik === undefined) continue;
            let param = {
                target: i,
                effector: ik.effector,
                iteration: ik.iteration,
                maxAngle: ik.maxAngle,
                links: []
            };
            for (let j = 0, jl = ik.links.length; j < jl; j++) {
                let link = {};
                link.index = ik.links[j].index;
                link.enabled = true;
                if (ik.links[j].angleLimitation === 1) {
                    // Revert if rotationMin/Max doesn't work well
                    // link.limitation =  BABYLON.Vector3.FromArray(1.0, 0.0, 0.0 );
                    var rotationMin = ik.links[j].lowerLimitationAngle;
                    var rotationMax = ik.links[j].upperLimitationAngle;
                    // Convert Left to Right coordinate by myself because
                    // MMDParser doesn't convert. It's a MMDParser's bug
                    var tmp1 = - rotationMax[0];
                    var tmp2 = - rotationMax[1];
                    rotationMax[0] = - rotationMin[0];
                    rotationMax[1] = - rotationMin[1];
                    rotationMin[0] = tmp1;
                    rotationMin[1] = tmp2;
                    link.rotationMin = BABYLON.Vector3.FromArray(rotationMin);
                    link.rotationMax = BABYLON.Vector3.FromArray(rotationMax);
                }
                param.links.push(link);
            }
            iks.push(param);
        }
    }
    // grants
    var grants = [];
    if (data.metadata.format === 'pmx') {
        for (let i = 0; i < data.metadata.boneCount; i++) {
            var boneData = data.bones[i];
            var grant = boneData.grant;
            if (grant === undefined) continue;
            let param = {
                index: i,
                parentIndex: grant.parentIndex,
                ratio: grant.ratio,
                isLocal: grant.isLocal,
                affectRotation: grant.affectRotation,
                affectPosition: grant.affectPosition,
                transformationClass: boneData.transformationClass
            };
            grants.push(param);
        }
        grants.sort(function (a, b) {
            return a.transformationClass - b.transformationClass;
        });

    }
    var rigidBodies = [];
    for (let i = 0; i < data.metadata.rigidBodyCount; i++) {
        var rigidBody = data.rigidBodies[i];
        let params = {};
        for (var key in rigidBody) {
            params[key] = rigidBody[key];
        }
        if (data.metadata.format === 'pmx') {
            if (params.boneIndex !== - 1) {
                let bone = data.bones[params.boneIndex];
                params.position[0] -= bone.position[0];
                params.position[1] -= bone.position[1];
                params.position[2] -= bone.position[2];
            }
        }
        rigidBodies.push(params);
    }
    var constraints = [];
    for (let i = 0; i < data.metadata.constraintCount; i++) {
        var constraint = data.constraints[i];
        let params = {};
        for (let key in constraint) {
            params[key] = constraint[key];
        }
        var bodyA = rigidBodies[params.rigidBodyIndex1];
        var bodyB = rigidBodies[params.rigidBodyIndex2];
        if (bodyA.type !== 0 && bodyB.type === 2) {
            if (bodyA.boneIndex !== - 1 && bodyB.boneIndex !== - 1 &&
                data.bones[bodyB.boneIndex].parentIndex === bodyA.boneIndex) {
                bodyB.type = 1;
            }
        }
        constraints.push(params);
    }
    ////////-------- 网格解析 --------//////// 
    var mesh = new BABYLON.Mesh(data.metadata.modelName),
        multimat = new BABYLON.MultiMaterial(data.metadata.modelName, scene);
    mesh.useBones = true;
    mesh.receiveShadows = true;
    multimat.needDepthPrePass = true;
    // 设置顶点
    var vertex = new BABYLON.VertexData();
    vertex.uvs = uvs;
    vertex.indices = indices;
    vertex.normals = normals;
    vertex.positions = positions;
    vertex.matricesIndices = matricesIndices;
    vertex.matricesWeights = matricesWeights;
    // 添加顶点
    vertex.applyToMesh(mesh, true);
    mesh.subMeshes = [];
    // 分割子网格
    for (let i = 0, faceCount = 0; i < data.metadata.materialCount; i++) {
        var Materials = data.materials[i];
        // 分割网格
        mesh.subMeshes[i] = new BABYLON.SubMesh(i, 0, vertex, faceCount * 3, Materials.faceCount * 3, mesh);
        mesh.subMeshes[i].id = Materials.name;
        // 创建贴图
        var mat = new BABYLON.StandardMaterial(Materials.name, scene);
        mat.backFaceCulling = false;
        mat.useLogarithmicDepth = true;
        //mat.wireframe = true; // 线框
        // 创建材料
        mat.diffuseColor = new BABYLON.Color3.FromArray(Materials.diffuse);
        mat.specularColor = new BABYLON.Color3.FromArray(Materials.specular);
        mat.emissiveColor = new BABYLON.Color3.FromArray(Materials.ambient);
        // 贴图信息
        if (data.metadata.format === 'pmx') {
            if (Materials.textureIndex !== - 1) {
                mat.diffuseTexture = textures[Materials.textureIndex];
            }
            if (Materials.envTextureIndex !== - 1 && (Materials.envFlag === 1 || Materials.envFlag == 2)) {
                mat.specularTexture = textures[Materials.envTextureIndex];
            }
            if (Materials.toonIndex === - 1 || Materials.toonFlag !== 0) {
                mat.ambientTexture = new BABYLON.Texture($.Maps[Materials.toonIndex + 1]);
            }
        } else {
            if (Materials.fileName) {
                var fileName = Materials.fileName;
                var fileNames = fileName.split('*');
                mat.diffuseTexture = textures[fileNames[0]];
                if (fileNames.length > 1) {
                    mat.specularTexture = textures[fileNames[1]];
                }
            }
            mat.ambientTexture = new BABYLON.Texture($.Maps[Materials.toonIndex + 1]);
        }
        mat.alphaMode = Materials.envFlag === 1 ? 4 : 1;
        // 设置贴图
        multimat.subMaterials.push(mat);
        // 网格计数
        faceCount += Materials.faceCount;
    }
    // 设置材质
    mesh.material = multimat;
    ////////-------- 骨骼解析 --------//////// 
    var Skeleton = new BABYLON.Skeleton(data.metadata.modelName + "-根骨骼", "-1", scene);
    // 注册骨骼
    mesh.skeleton = Skeleton;
    // 解析骨骼
    for (let i = 0; i < data.metadata.boneCount; i++) {
        var bones = data.bones[i], parentBone = null,
            matrix = new BABYLON.Matrix();
        // 初始化矩阵
        matrix.m[0] = 1;
        matrix.m[5] = 1;
        matrix.m[10] = 1;
        matrix.m[15] = 1;
        // 设置骨骼顶点
        if (bones.parentIndex !== -1) {
            matrix.m[12] = bones.position[0] - data.bones[bones.parentIndex].position[0];
            matrix.m[13] = bones.position[1] - data.bones[bones.parentIndex].position[1];
            matrix.m[14] = bones.position[2] - data.bones[bones.parentIndex].position[2];
            parentBone = Skeleton.bones[bones.parentIndex];
        } else {
            matrix.m[12] = bones.position[0];
            matrix.m[13] = bones.position[1];
            matrix.m[14] = bones.position[2];
        }
        // 创建骨骼
        var bone = new BABYLON.Bone(bones.name, Skeleton, parentBone, matrix);
        bone.id = i;
    }
    // 调试骨骼
    // var skeletonViewer = new BABYLON.Debug.SkeletonViewer(Skeleton, mesh, scene);
    // 修改骨骼颜色
    // skeletonViewer.isEnabled = true;
    // skeletonViewer.color = BABYLON.Color3.Red();
    ////////-------- 表情解析 --------//////// 
    var Expressions = [], backupVertices = {};
    for (let i = 0; i < data.metadata.morphCount; i++) {
        Expressions.push(data.morphs[i].name);
    }
    // 表情设置函数
    var SetExpressions = function (id, ratio) {
        let vertices = mesh.getVerticesData(BABYLON.VertexBuffer.PositionKind);
        if (id != 0) {
            let morph = data.morphs[id], backup = {};
            for (let i = 0; i < morph.elementCount; i++) {
                let element = morph.elements[i];
                let index;
                if (data.metadata.format === 'pmd') {
                    index = data.morphs[0].elements[element.index].index;
                } else {
                    index = element.index;
                }
                index = index * 3;
                if (backupVertices[index]) {
                    // 还原顶点
                    vertices[index] -= backupVertices[index][0];
                    vertices[index + 1] -= backupVertices[index][1];
                    vertices[index + 2] -= backupVertices[index][2];
                }
                // 设置顶点
                position = [element.position[0] * ratio, element.position[1] * ratio, element.position[2] * ratio];
                vertices[index] += position[0];
                vertices[index + 1] += position[1];
                vertices[index + 2] += position[2];
                backupVertices[index] = position;
            }
        } else {
            for (let index in backupVertices) {
                var i = Number(index);
                vertices[i] -= backupVertices[index][0];
                vertices[i + 1] -= backupVertices[index][1];
                vertices[i + 2] -= backupVertices[index][2];
            }
            backupVertices = {};
        }
        mesh.updateVerticesData(BABYLON.VertexBuffer.PositionKind, vertices);
    };
    // 返回信息
    return {
        morphs: data.morphs,
        mesh: mesh,
        iks: iks,
        grants: grants,
        SetExpressions: SetExpressions,
        Expressions: Expressions,
        rigidBodies: rigidBodies,
        constraints: constraints,
        vertex: vertex,
        Skeleton: Skeleton
    };
};